今天一樣是 jinja 的內容,會講到模板的繼承。這個在網頁有固定排版或是格式的時候很好用,不用一直重複複製貼上。就算網頁沒有固定排版,基本上 head、要引入的 CSS、JS 也可能有重複。簡單來說,我覺得這是一個一定會用到的功能,後面開始寫專案的時候也會用到。
先來看個範例。base.html 和 index.html 都必須放在 templates/ 裡面。
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index_page():
    user = "Cat"
    return render_template("index.html", user=user)
app.run(host="127.0.0.1", port=8080, debug=True)
base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <nav>
        nav
    </nav>
    <main>
        {% block content %}
        {% endblock %}
    </main>
    <footer>
        footer
    </footer>
</body>
index.html
{% extends "base.html" %}
{% block title %}Index Page{% endblock %}
{% block content %}
<p>
    {{ user }}
</p>
{% endblock %}
app.py 和之前差不多,就把 user 傳入 jinja 處理。
base.html 裡面出現了 {% block %},他代表這是一個可以自由發揮的區塊,也就是非排版的部分,而 block 後面接的 title 和 content 則是這個 block 的名稱。我們在 base.html 定義好一些區塊,然後交給其他模板去寫,以此處為例就是下面的 index.html。
最後我們看到 index.html,他在最一開始就宣告說這個模板是從 base.html 繼承來的,所以他會照著剛剛 base.html 的格式生成已經寫好的內容。接下來,我們把剛剛在 base.html 留下來的 block 一一填好。首先是 title,就跟剛剛的寫法一樣,需要 {% block title %}{% endblock %},但這次中間需要放入自己想要的網站標題,此處就用 Index Page 為例。下一個是 content,在這邊我們就把 flask 傳過來的 user 塞進去。這樣就完成了樣板的繼承,這時候打開瀏覽器應該可以看到它顯示出 nav、剛剛傳入的 user、footer。
最後我們來實作未來要用到的 base.html。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{% endblock %}</title>
    {% block extra_import %}
    {% endblock %}
</head>
<body>
    <nav>
        <!-- unauthorized -->
        <span><a href="/login">Login</a></span>
        <span><a href="/register">Register</a></span>
        <!-- normal user -->
        <span><a href="/dashboard">Dashboard</a></span>
        <span><a href="/posts">All posts</a></span>
        <span><a href="/post/add">Add post</a></span>
        <span><a href="/setting">Setting</a></span>
        <!-- admin -->
        <span><a href="/admin_dashboard/posts">Admin Dashboard for posts</a></span>
        <span><a href="/admin_dashboard/comments">Admin Dashboard for comments</a></span>
        <span><a href="/manage_user">Manage User</a></span>
    </nav>
    <main>
        {{ display_msg(get_flashed_messages(with_categories=True)) }}
        {% block content %}
        {% endblock %}
    </main>
    <footer>
        © siriuskoan
    </footer>
</body>
</html>
這裡多數都是跟剛剛差不多的內容,比較特別的是我多放了一個 extra_import,在這裡可以放一些 CSS、JS 等等的,而他們要放在哪裡則是明天的主題。
在 nav 的部分我放了很多連結,同時也幫他們上了註解,很明顯我們不可能對所有人都顯示出一樣的 nav,所以在未來我們會在這裡做一些判斷來決定到底要怎麼顯示 nav。
有了這個模板之後,我們未來所有的 HTML 都會繼承自他,可以大幅減少重複的內容。